package com.travelingsalesmen.post;

import java.util.HashMap;
import java.util.Random;

public abstract class Payment {

    private static final HashMap<String, Class<? extends Payment>> paymentMap = new HashMap<String, Class<? extends Payment>>();
    protected boolean wasSuccessful;
    private String failureReason;

    static {
        paymentMap.put("CASH", CashPayment.class);
        paymentMap.put("CREDIT", CreditPayment.class);
        paymentMap.put("CHECK", CheckPayment.class);
    }
    private double total;
    private String extraInfo;

    public Payment() {
    }

    public boolean wasSuccessful() {
        return wasSuccessful;
    }

    public String getFailureReason() {
        return failureReason;
    }

    protected void setFailureReason(String failureReason) {
        this.failureReason = failureReason;
    }

    public void setExtraInfo(String extraInfo) {
        this.extraInfo = extraInfo;
    }

    protected String getExtraInfo() {
        return extraInfo;
    }

    abstract boolean makePayment(double total);

    public static Payment createPayment(String type) {
        try {
            Class<? extends Payment> subclass = paymentMap.get(type);
            Payment payment = (Payment) subclass.newInstance();
            return payment;
        } catch (Exception e) {
            throw new RuntimeException("Invalid payment");
        }
    }
}

class CashPayment extends Payment {

    double tendered;
    double change;

    public CashPayment() {
        super();
    }

    @Override
    public boolean makePayment(double total) {
        if (getExtraInfo().charAt(0) == '$') {
            tendered = Double.parseDouble(getExtraInfo().substring(1));
        } else {
            tendered = Double.parseDouble(getExtraInfo());
        }

        change = tendered - total;

        if (((total - tendered) < .01)) {
            wasSuccessful = true;
            return true;
        } else {
            setFailureReason("Insufficient funds");
            return false;
        }
    }

    public double getTendered() {
        return tendered;
    }

    public double getChange() {
        return change;
    }
}

class CreditPayment extends Payment {

    long accountNumber;

    public CreditPayment() {
        super();
    }

    @Override
    public boolean makePayment(double total) {
        accountNumber = Long.parseLong(getExtraInfo());
        if (isAuthorized()) {
            wasSuccessful = true;
            return true;
        } else {
            setFailureReason("Credit card authorization failure");
            return false;
        }
    }

    private boolean isAuthorized() {
        // 10% failure rate for auth.
        return new Random().nextInt(100) >= 10;
    }

    public long getAccountNumber() {
        return accountNumber;
    }
}

class CheckPayment extends Payment {

    public CheckPayment() {
        super();
    }

    @Override
    public boolean makePayment(double total) {
        if (isAuthorized()) {
            wasSuccessful = true;
            return true;
        } else {
            setFailureReason("Check authorization failure");
            return false;
        }
    }

    private boolean isAuthorized() {
        // 10% failure rate for auth.
        return new Random().nextInt(100) >= 10;
    }
}